/* 
 * File:   LPLibrary.h
 * Author: sergey
 *
 * Created on 9 Декабрь 2009 г., 21:29
 *
 * LP-библиотека, которая используется в первую очередь только лишь для
 * хранения функций от 5 переменных, но так же может использоваться для
 * функций от меньшего числа аргументов.
 */

#ifndef _LPLIBRARY_H
#define	_LPLIBRARY_H

#include <vector>
#include "LibraryInterface.h"
#include "BooleanFunction.h"
#include <map>
#include <queue>

/*
 * LPModify - функция, которая для функции f делает возможные операции модификации
 * в lp-классе по va-му аргументу. Она помечает все функции в массиве done и
 * добавляет их в очередь q.
 */
template <typename LIBINFO, int va>
inline void LPModify(std::vector<bool> &done, std::queue<UInt64> &q, const UInt64 f) {
    UInt64 tf = f;
    BooleanFunction<LIBINFO>::template derivative1<va>((UInt32&)tf);
    if (!done[tf]) {
        done[tf] = true;
        q.push(tf);
    }
    tf = f;
    BooleanFunction<LIBINFO>::template derivative2<va>(tf);
    if (!done[tf]) {
        done[tf] = true;
        q.push(tf);
    }
    tf = f;
    BooleanFunction<LIBINFO>::template derivative3<va>(tf);
    if (!done[tf]) {
        done[tf] = true;
        q.push(tf);
    }
    tf = f;
    BooleanFunction<LIBINFO>::template derivative4<va>(tf);
    if (!done[tf]) {
        done[tf] = true;
        q.push(tf);
    }
    tf = f;
    BooleanFunction<LIBINFO>::template negation<va>(tf);
    if (!done[tf]) {
        done[tf] = true;
        q.push(tf);
    }
    tf = f;
    BooleanFunction<LIBINFO>::template transposition<va>(tf);
    if (!done[tf]) {
        done[tf] = true;
        q.push(tf);
    }
}

/*
 * Класс insertLPModify делает lp-модификации для функции f и добавляет все
 * функции из класса в очередь q. Функции, которые уже были обработаны
 * "помечаются" в массиве done.
 *
 * Этот класс необходимо описывать для различных вариантов n.
 */
template <typename LIBINFO, int n> class insertLPModify;

template <typename LIBINFO> class insertLPModify<LIBINFO, 1> {
public: inline static void go(std::vector<bool> &done, std::queue<UInt64> &q, const UInt64 f) {
    LPModify<LIBINFO, 0>(done, q, f);
}};

template <typename LIBINFO> class insertLPModify<LIBINFO, 2> {
public: inline static void go(std::vector<bool> &done, std::queue<UInt64> &q, const UInt64 f) {
    LPModify<LIBINFO, 0>(done, q, f);
    LPModify<LIBINFO, 1>(done, q, f);
}};

template <typename LIBINFO> class insertLPModify<LIBINFO, 3> {
public: inline static void go(std::vector<bool> &done, std::queue<UInt64> &q, const UInt64 f) {
    LPModify<LIBINFO, 0>(done, q, f);
    LPModify<LIBINFO, 1>(done, q, f);
    LPModify<LIBINFO, 2>(done, q, f);
}};

template <typename LIBINFO> class insertLPModify<LIBINFO, 4> {
public: inline static void go(std::vector<bool> &done, std::queue<UInt64> &q, const UInt64 f) {
    LPModify<LIBINFO, 0>(done, q, f);
    LPModify<LIBINFO, 1>(done, q, f);
    LPModify<LIBINFO, 2>(done, q, f);
    LPModify<LIBINFO, 3>(done, q, f);
}};

template <typename LIBINFO> class insertLPModify<LIBINFO, 5> {
public: inline static void go(std::vector<bool> &done, std::queue<UInt64> &q, const UInt64 f) {
    LPModify<LIBINFO, 0>(done, q, f);
    LPModify<LIBINFO, 1>(done, q, f);
    LPModify<LIBINFO, 2>(done, q, f);
    LPModify<LIBINFO, 3>(done, q, f);
    LPModify<LIBINFO, 4>(done, q, f);
}};

/*
 * LPLibrary - класс для хранения lp-библиотеки.
 */
template <typename LIBINFO>
class LPLibrary:public LibraryInterface {
public:
    LPLibrary() { 
        done.resize(LIBINFO::fC);
    }
    ~LPLibrary() { }
    inline void get(const UInt64 f, UInt8 &s) {
        if (isSet(f)) {
            UInt64 c;
            getBFClass(f, c);
            s = bfclass[f];
        } else {
            s = -1;
        }
    }
    inline void set(const UInt64 f, const UInt8 s) {
        if (!isSet(f)) {
            done[f] = true;
            UInt64 c = s;
            bfclass[f] = c;
            getBFClass(f, c);            
        }
    }
    inline bool isSet(const UInt64 f) {
        return done[f];
    }
    void load(const std::string filename) {
        this->readFileOpen(filename.c_str());
        while(!this->iseof()) {
            UInt64 f; UInt8 s;
            this->readInt(f);
            this->readInt(s);
            bfclass[f] = s;
        }
        this->readFileClose();
    }
    void save(const std::string filename) {
        this->writeFileOpen(filename.c_str());
        for(std::map<UInt64, UInt8>::iterator i = bfclass.begin(); i != bfclass.end(); i++) {
            this->writeInt(i->first);
            this->writeInt(i->second);
        }
        this->writeFileClose();
    }
    std::map<UInt64, UInt8> bfclass;
protected:
    inline void getBFClass(const UInt64 f, UInt64 &c) {
        std::queue<UInt64> q;
        q.push(f);
        c = f;
        done[f] = true;
        while(!q.empty()) {
            UInt64 tf = q.front(); q.pop();
            insertLPModify<LIBINFO, LIBINFO::n>::go(done, q, tf);
        }
    }
    std::vector<bool> done;
};

#endif	/* _LPLIBRARY_H */

